package com.cicigodd.brick.plug.upload.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cicigodd.brick.plug.common.exception.BusinessException;
import com.cicigodd.brick.plug.common.exception.ServiceException;
import com.cicigodd.brick.plug.upload.api.FileOperatorApi;
import com.cicigodd.brick.plug.upload.entity.SysFile;
import com.cicigodd.brick.plug.upload.mapper.SysFileMapper;
import com.cicigodd.brick.plug.upload.request.SysFileDownRequest;
import com.cicigodd.brick.plug.upload.request.SysFileIdDownRequest;
import com.cicigodd.brick.plug.upload.request.SysFileRequest;
import com.cicigodd.brick.plug.upload.response.FileResponse;
import com.cicigodd.brick.plug.upload.response.SysFileResponse;
import com.cicigodd.brick.plug.upload.service.SysFileService;
import com.cicigodd.brick.plug.upload.util.PdfFileTypeUtil;
import com.cicigodd.brick.plug.upload.util.PicFileTypeUtil;
import com.cicigodd.brick.plug.upload.util.VideoFileTypeUtil;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.activation.MimetypesFileTypeMap;
import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 文件管理
 *
 * @author cicigodd
 * @date 2022/09/23 08:42:00
 */
@Service
@Slf4j
public class SysFileServiceImpl extends ServiceImpl<SysFileMapper, SysFile> implements SysFileService {

    @Resource
    private FileOperatorApi fileOperatorApi;

    /**
     * 根据文件id获取文件访问地址
     *
     * @param fileId
     * @return
     */
    @Override
    public String getFileUrl(Integer fileId) {
        // 根据文件id获取文件信息结果集
        SysFile sysFile = this.getById(fileId);
        if (ObjectUtil.isEmpty(sysFile)) {
            return "";
        }
        // 生成文件访问地址
        String fileUrl = fileOperatorApi.getFileUrl(sysFile.getFileBucket(), sysFile.getFileObjectName());
        return fileUrl;
    }

    /**
     * 根据多个文件id获取文件访问地址
     *
     * @param fileIds
     * @return
     */
    @Override
    public List<String> getFileUrlByIds(String fileIds) {
        List<String> fileUrlList = new ArrayList<>();
        String[] fileList = fileIds.split(",");
        List<SysFile> sysFileList = this.listByIds(Arrays.asList(fileList));
        sysFileList.forEach(item -> {
            String fileUrl = fileOperatorApi.getFileUrl(item.getFileBucket(), item.getFileObjectName());
            fileUrlList.add(fileUrl);
        });
        return fileUrlList;
    }

    @Override
    public List<FileResponse> getFileObjectByIds(String fileIds) {
        List<FileResponse> fileList = new ArrayList<>();
        if (ObjectUtil.isNotEmpty(fileIds)) {
            List<SysFile> sysFileList = this.listByIds(Arrays.asList(fileIds.split(",")));
            sysFileList.forEach(item -> {
                Integer id = item.getId();
                String name = item.getFileOriginName();
                String url = fileOperatorApi.getFileUrl(item.getFileBucket(), item.getFileObjectName());
                FileResponse fileResponse = new FileResponse();
                fileResponse.setId(id);
                fileResponse.setName(name);
                fileResponse.setUrl(url);
                fileList.add(fileResponse);
            });
        }
        return fileList;
    }

    /**
     * 上传文件
     *
     * @param file
     * @return
     */
    @Override
    public SysFileResponse upload(MultipartFile file) {
        //  保存文件到磁盘
        SysFile sysFile = this.createSysFile(file);
        // 保存文件信息到数据库
        this.save(sysFile);
        // 返回文件信息体
        SysFileResponse sysFileResponse = new SysFileResponse();
        sysFileResponse.setId(sysFile.getId());
        sysFileResponse.setName(sysFile.getFileOriginName());
        sysFileResponse.setUrl(sysFile.getFileUrl());
        return sysFileResponse;
    }

    /**
     * 保存文件到磁盘
     *
     * @param file
     * @return
     */
    private SysFile createSysFile(MultipartFile file) {

        // 生成文件的唯一id
        Long fileId = IdWorker.getId();

        // 获取文件原始名称
        String originalFilename = file.getOriginalFilename();

        // 获取文件后缀（不包含点）
        String fileSuffix = null;
        if (ObjectUtil.isNotEmpty(originalFilename)) {
            fileSuffix = StrUtil.subAfter(originalFilename, ".", true);
        }

        // 生成文件的最终名称
        String finalFileName = fileId + "." + fileSuffix;

        // 桶名
        String fileBucket = "defaultBucket";

        // 存储文件
        try {
            if (fileSuffix.equals("jpg") || fileSuffix.equals("jpeg") || fileSuffix.equals("png")) {
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                Thumbnails.of(file.getInputStream())
                        .scale(1f) //图片大小（长宽）压缩比例 从0-1，1表示原图
                        .outputQuality(0.5f) //图片质量压缩比例 从0-1，越接近1质量越好
                        .toOutputStream(byteArrayOutputStream);
                InputStream sbs = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
                fileOperatorApi.storageFile(fileBucket, finalFileName, sbs);
            } else {
                fileOperatorApi.storageFile(fileBucket, finalFileName, file.getInputStream());
            }
        } catch (IOException e) {
            throw new ServiceException("上传文件操作异常，具体信息为：" + e.getMessage());
        }

        // 计算文件大小kb
        long fileSizeKb = Convert.toLong(NumberUtil.div(new BigDecimal(file.getSize()), BigDecimal.valueOf(1024)).setScale(0, BigDecimal.ROUND_HALF_UP));

        // 计算文件大小信息
        String fileSizeInfo = FileUtil.readableFileSize(file.getSize());

        // 封装存储文件信息（上传替换公共信息）
        SysFile sysFile = new SysFile();
        sysFile.setFileBucket(fileBucket);
        sysFile.setFileOriginName(originalFilename);
        sysFile.setFileSuffix(fileSuffix);
        sysFile.setFileSizeKb(fileSizeKb);
        sysFile.setFileSizeInfo(fileSizeInfo);
        sysFile.setFileObjectName(finalFileName);
        String fileUrl = fileOperatorApi.getFileUrl(sysFile.getFileBucket(), sysFile.getFileObjectName());
        sysFile.setFileUrl(fileUrl);
        return sysFile;
    }

    /**
     * 文件预览
     *
     * @param sysFileRequest
     * @param response
     */
    @Override
    public void previewByBucketAndObjName(SysFileRequest sysFileRequest, HttpServletResponse response) {

        // 获取文件流
        BufferedInputStream bufferedInputStream;
        try {
            bufferedInputStream = fileOperatorApi.getBufferedInputStream(sysFileRequest.getFileBucket(), sysFileRequest.getFileObjectName());
        } catch (Exception e) {
            throw new ServiceException("该条文件信息记录不存在");
        }

        try {
            String fileSuffix = FileUtil.getSuffix(sysFileRequest.getFileObjectName());
            if (PicFileTypeUtil.getFileImgTypeFlag(fileSuffix)) {
                response.setContentType(MediaType.IMAGE_PNG_VALUE);
            } else if (PdfFileTypeUtil.getFilePdfTypeFlag(fileSuffix)) {
                response.setContentType(MediaType.APPLICATION_PDF_VALUE);
            } else if (VideoFileTypeUtil.getFileVideoTypeFlag(fileSuffix)) {
                response.setContentType("video/mp4");
            }else {
                response.setContentType("application/octet-stream");
            }
            // 写入文件流
            ServletOutputStream outputStream = response.getOutputStream();
            byte[] b = new byte[1024];
            int len;
            //从输入流中读取一定数量的字节，并将其存储在缓冲区字节数组中，读到末尾返回-1
            while ((len = bufferedInputStream.read(b)) > 0) {
                outputStream.write(b, 0, len);
            }
            bufferedInputStream.close();
        } catch (Exception e) {
            throw new ServiceException("预览失败，文件错误");
        }

    }


    /**
     * 文件下载
     *
     * @param sysFileIdDownRequest
     * @param response
     */
    @Override
    public void downloadById(SysFileIdDownRequest sysFileIdDownRequest, HttpServletResponse response) {
        // 根据文件id获取文件信息结果集
        SysFile sysFile = this.getById(sysFileIdDownRequest.getId());
        if (ObjectUtil.isEmpty(sysFile)) {
            throw new BusinessException("文件未找到");
        }
        try {
            // 获取文件流
            BufferedInputStream bufferedInputStream = fileOperatorApi.getBufferedInputStream(sysFile.getFileBucket(), sysFile.getFileObjectName());
            String contentType = new MimetypesFileTypeMap().getContentType(sysFile.getFileObjectName());
            response.setHeader("content-type", contentType);
            response.setContentType(contentType);
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(sysFile.getFileOriginName(), "UTF-8"));
            // 写入文件流
            ServletOutputStream outputStream = response.getOutputStream();
            byte[] b = new byte[1024];
            int len;
            while ((len = bufferedInputStream.read(b)) > 0) {
                outputStream.write(b, 0, len);
            }
            bufferedInputStream.close();
            outputStream.close();
        } catch (Exception e) {
            throw new ServiceException("下载失败，文件已不存在");
        }
    }

    /**
     * 文件下载
     *
     * @param sysFileDownRequest
     * @param response
     */
    @Override
    public void download(SysFileDownRequest sysFileDownRequest, HttpServletResponse response) {
        // 根据文件id获取文件信息结果集
        String fileBucket = sysFileDownRequest.getFileBucket();
        String fileObjectName = sysFileDownRequest.getFileObjectName();
        try {
            // 获取文件流
            BufferedInputStream bufferedInputStream = fileOperatorApi.getBufferedInputStream(fileBucket, fileObjectName);
            String contentType = new MimetypesFileTypeMap().getContentType(fileObjectName);
            response.setHeader("content-type", contentType);
            response.setContentType(contentType);
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileObjectName, "UTF-8"));
            // 写入文件流
            ServletOutputStream outputStream = response.getOutputStream();
            byte[] b = new byte[1024];
            int len;
            while ((len = bufferedInputStream.read(b)) > 0) {
                outputStream.write(b, 0, len);
            }
            bufferedInputStream.close();
            outputStream.close();
        } catch (Exception e) {
            throw new ServiceException("下载失败，文件已不存在");
        }
    }
}
